package top.wshape1.shiziapi.project.common.aspect;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import top.wshape1.shiziapi.common.utils.R;
import top.wshape1.shiziapi.project.common.annotation.EditHistoryLog;
import top.wshape1.shiziapi.project.common.constant.DocumentConstant;
import top.wshape1.shiziapi.project.common.constant.EditConstant;
import top.wshape1.shiziapi.project.domain.DocVersion;
import top.wshape1.shiziapi.project.domain.EditHistory;
import top.wshape1.shiziapi.project.domain.Project;
import top.wshape1.shiziapi.project.handler.LoginContextHandler;
import top.wshape1.shiziapi.project.mapper.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Date;

/**
 * @author Wshape1
 * @version 1.0
 * @description
 * @since 2024-12-25
 */

@Component
@Aspect
@RequiredArgsConstructor
@Slf4j
public class EditHistoryLogAspect {

    private final EditHistoryMapper editHistoryMapper;

    private final ProjectMapper projectMapper;

//    private final ProjectMembersMapper projectMembersMapper;

    private final MarkdownMapper markdownMapper;

    private final RtfMapper rtfMapper;

    private final ApiMapper apiMapper;

    private final DocVersionMapper versionMapper;

    private final FolderMapper folderMapper;

    private final MindmapMapper mindmapMapper;


    @Pointcut("@annotation(top.wshape1.shiziapi.project.common.annotation.EditHistoryLog)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("切面切面启动。。。");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Object[] args = joinPoint.getArgs();
        Parameter[] parameters = method.getParameters();

        EditHistoryLog historyLog = method.getAnnotation(EditHistoryLog.class);
        Long projectId = (Long) getParamValue(parameters, args, historyLog.projectIdParamName());
        if (projectId == null) {
            throw new RuntimeException("项目id为空");
        }
        Long docId = (Long) getParamValue(parameters, args, historyLog.docIdParamName());
        Object docTypeObj = this.getParamValue(parameters, args, historyLog.docTypeParamName());
        Integer docType = null;
        if (docTypeObj != null) {
            docType = DocumentConstant.typeStringToInt(docTypeObj.toString());
        }


        EditHistory editHistory = new EditHistory();
        editHistory.setEditType(historyLog.type());

        switch (editHistory.getEditType()) {
            case EditConstant.EDIT_TYPE_MODIFY -> {
                Project project = projectMapper.selectOne(new LambdaQueryWrapper<Project>()
                        .eq(Project::getId, projectId));
                editHistory.setEditBefore(JSON.toJSONString(project));
            }
            case EditConstant.EDIT_TYPE_CREATE_DOC -> {
                // /
            }
            case EditConstant.EDIT_TYPE_DELETE_DOC ->
                    editHistory.setEditBefore(JSON.toJSONString(this.getDocument(docId, docType)));
            case EditConstant.EDIT_TYPE_RENAME_DOC -> {
                String docName = getDocumentName(docId, docType);
                editHistory.setEditBefore(docName);
            }
            case EditConstant.EDIT_TYPE_SAVE_VERSION -> {
                //
            }
            case EditConstant.EDIT_TYPE_DELETE_VERSION -> {
                DocVersion version = versionMapper.selectById(this.getParamValue(parameters, args, historyLog.versionIdParamName()).toString());
                editHistory.setEditBefore(JSON.toJSONString(version));
                docType = version.getDocType();
                docId = version.getDocId();
            }
            case EditConstant.EDIT_TYPE_MEMBER_CHANGE -> {
                // //
            }
            default -> throw new RuntimeException("未知的编辑类型");
        }


        Object proceed = joinPoint.proceed();

        if (proceed instanceof R r) {
            if (!r.isSuccessful()) {
                return proceed;
            }
        }

        editHistory.setProjectId(projectId);
        editHistory.setDocId(docId);
        editHistory.setDocType(docType);
        editHistory.setEditorId(LoginContextHandler.getUserId());
        editHistory.setCreateTime(new Date());
        editHistory.setUpdateTime(new Date());

        switch (editHistory.getEditType()) {
            case EditConstant.EDIT_TYPE_MODIFY -> {
                Project project = projectMapper.selectOne(new LambdaQueryWrapper<Project>()
                        .eq(Project::getId, projectId));
                editHistory.setEditAfter(JSON.toJSONString(project));
            }
            case EditConstant.EDIT_TYPE_CREATE_DOC -> {
                if (proceed instanceof R r) {
                    if (r.isSuccessful()) {
                        if (r.getData() instanceof Long id) {
                            editHistory.setDocId(id);
                            editHistory.setEditAfter(JSON.toJSONString(getDocument(id, docType)));
                        }
                    }
                }
            }
            case EditConstant.EDIT_TYPE_RENAME_DOC -> {
                editHistory.setEditAfter(this.getParamValue(parameters, args, historyLog.docNameParamName()).toString());
            }
            case EditConstant.EDIT_TYPE_SAVE_VERSION -> {
                if (proceed instanceof R r) {
                    if (r.isSuccessful()) {
                        if (r.getData() instanceof Long id) {
                            editHistory.setDocType((Integer) docTypeObj);
                            DocVersion docVersion = versionMapper.selectById(id);
                            editHistory.setEditAfter(JSON.toJSONString(docVersion));
                        }
                    }
                }
            }
            case EditConstant.EDIT_TYPE_DELETE_VERSION -> {
            }
            case EditConstant.EDIT_TYPE_DELETE_DOC -> {
            }
            case EditConstant.EDIT_TYPE_MEMBER_CHANGE -> {
                // //
            }
        }

        editHistoryMapper.insert(editHistory);

        return proceed;
    }

    private @Nullable String getDocumentName(Long docId, Integer docType) {
        return switch (docType) {
            case DocumentConstant.INT_MARKDOWN_TYPE -> markdownMapper.selectById(docId).getName();
            case DocumentConstant.INT_RTF_TYPE -> rtfMapper.selectById(docId).getName();
            case DocumentConstant.INT_API_TYPE -> apiMapper.selectById(docId).getName();
            case DocumentConstant.INT_FOLDER_TYPE -> folderMapper.selectById(docId).getName();
            case DocumentConstant.INT_MINDMAP_TYPE -> mindmapMapper.selectById(docId).getName();
            default -> null;
        };
    }

    private Object getParamValue(Parameter[] parameters, Object[] args, String pattern) throws NoSuchFieldException, IllegalAccessException {
        if (pattern == null || pattern.isEmpty())
            return null;
        String[] patterns = pattern.split("\\.");
        int index = -1;
        for (int i = 0; i < parameters.length; i++) {
            if (parameters[i].getName().equals(patterns[0])) {
                index = i;
                break;
            }
        }
        Object obj = null;
        if (index != -1) {
            obj = args[index];
            for (int i = 1; i < patterns.length; i++) {
                if (obj == null) {
                    break;
                }
                Field field = obj.getClass().getDeclaredField(patterns[i]);
                field.setAccessible(true);
                obj = field.get(obj);
            }
        }
        return obj;
    }

    private Object getDocument(Long docId, Integer docType) {
        return switch (docType) {
            case DocumentConstant.INT_MARKDOWN_TYPE -> markdownMapper.selectById(docId);
            case DocumentConstant.INT_RTF_TYPE -> rtfMapper.selectById(docId);
            case DocumentConstant.INT_API_TYPE -> apiMapper.selectById(docId);
            case DocumentConstant.INT_FOLDER_TYPE -> folderMapper.selectById(docId);
            case DocumentConstant.INT_MINDMAP_TYPE -> mindmapMapper.selectById(docId);
            default -> null;
        };
    }


}
